/******************************************************
* Team WB
* Brett Cate, William Kim
* 
* CPU ADT Source
*******************************************************/

#include "cpu.h"

/*
Instantiates and returns a pointer to a new CPU object.

parameter (the_error): pointer to the error code value for this function.

return value: A pointer to this CPU object.
*/
CPUPtr cpu_new(int* the_error) {
	CPUPtr cpu_ptr = (CPUPtr) malloc(sizeof(CPU));
	cpu_ptr->registerFile = registerFile_new();
	cpu_ptr->SW = register_new();
	cpu_ptr->alu = alu_new(cpu_ptr->SW, the_error);
	if (*the_error)
		return NULL;
	cpu_ptr->IR = register_new();
	cpu_ptr->MAR = register_new();
	cpu_ptr->MDR = register_new();
	cpu_ptr->PC = register_new();
	cpu_ptr->IOPORTS = memory_newIOMemory(40);
	cpu_ptr->mainMemory = memory_new();
	*cpu_ptr->registerFile[STACK_POINTER_REG_NUM] = 0xFFFF;
	cpu_ptr->cursorX = 0;
	cpu_ptr->cursorY = 27;
	
	cpu_ptr->macroState = FETCH;
	return cpu_ptr;
}

/*
Simulates a state machine using the CPU pointed to by the given CPUPtr,
and returns the value of the error code for the function.

parameter (the_cpu): Pointer the CPU to be used for this function.
parameter (the_debug_monitor_ptr): Pointer to the debug monitor whose
debug state is used by this function.

return value: The value of error code for this function.
*/
int cpu_run(CPUPtr the_cpu, struct DebugMonitor* the_debug_monitor_ptr) {
	int current_state, error, i;
	ushort opcode, opcode_mod;
	if (the_cpu == NULL || the_debug_monitor_ptr == NULL)
		return NULL_POINTER_ERROR;
	current_state = RUN;
	while (current_state != STOP && current_state != BREAK) {
		current_state = debugMonitor_getDebugState((DebugMonitorPtr)the_debug_monitor_ptr, &error);
		for (i = 0; i < ((DebugMonitorPtr)the_debug_monitor_ptr)->breakpoint_index; i++) {
			if (*the_cpu->PC == ((DebugMonitorPtr)the_debug_monitor_ptr)->breakPoints[i]) {
				current_state = BREAK;
			}
		}
		switch(the_cpu->macroState) {
			case FETCH:
				cpu_fetchInstruction(the_cpu);
				break;
			case DECODE:
				opcode = cpu_decodeInstruction(the_cpu,&error,&opcode_mod);
				break;
			case EXECUTE:
				cpu_executeInstruction(the_cpu,opcode,opcode_mod);
				break;
			case STOP:
				current_state = STOP;
				break;
			default:
				break;
		}

		switch(current_state) {
			case STEP:
				// step only after one complete cpu cycle
				if (the_cpu->macroState == FETCH) {
					debugMonitor_showAll(((DebugMonitorPtr)the_debug_monitor_ptr));
					getch();
				}
				break;
			default:
				break;
		}
		((DebugMonitorPtr)the_debug_monitor_ptr)->currentPCValue = *the_cpu->PC;
	}
	return NO_ERROR;
}

/*
Fetches the instruction from memory and places it in the IR, increments the PC, and returns
the error code for this function.

parameter (the_cpu): Pointer to the CPU whose IR the instruction is placed in.

return value: the int error code value for this function.
*/
int cpu_fetchInstruction(CPUPtr the_cpu) {
	int error;
	if (the_cpu == NULL)
		return NULL_POINTER_ERROR;
	// PC -> MAR
	error = cpu_setMAR(the_cpu, *the_cpu->PC);
	if (error)
		return error;
	 // MEM[MAR] -> IR
	cpu_setIR(the_cpu, memory_fetchWordFrom(the_cpu->mainMemory, *the_cpu->MAR, &error));
	if (error)
		return error;
	// PC + 1 -> PC
	error = cpu_incrementPC(the_cpu);
	if (error)
		return error;
	the_cpu->macroState = DECODE;
	return NO_ERROR;
}

/*
Increments the PC in the given CPU.

parameter (the_cpu): Pointer to the cpu whose PC is incremented.

return value: the int value of the error code of this function.
*/
int cpu_incrementPC(CPUPtr the_cpu) {
	return cpu_setPC(the_cpu, (*the_cpu->PC + 2));
}

/*
Returns the opcode of the current instruction as a ushort and places the mod value 
for the instruction at the address of the given ushort parameter.

parameter (the_cpu): Pointer to the CPU whose IR the opcode is gathered from.
parameter (the_error): Pointer to the value of the error code for this function.
parameter (the_opcode_mod): Pointer to the ushort value where the opcode mod is placed.

return value: The ushort value of the opcode of the instruction in the given CPU.
*/
ushort cpu_decodeInstruction(CPUPtr the_cpu, int* the_error, ushort* the_opcode_mod) {
	ushort opcode;
	if (the_cpu == NULL) {
		*the_error = NULL_POINTER_ERROR;
		return 0;
	}
	opcode = cpu_getIR(the_cpu, the_error) >> 11;
	if (*the_error) {
		return 0;
	}
	opcode = opcode & 0x001F;
	*the_opcode_mod = cpu_getIR(the_cpu, the_error) & 0x0007;
	if (*the_error) {
		return 0;
	}
	if (opcode == OPCODE_ALU || opcode == OPCODE_ALU_SHL || opcode == OPCODE_ALU_SHR)
		*the_error = cpu_fetchOperands(the_cpu);
	if (*the_error) {
		return 0;
	}
	the_cpu->macroState = EXECUTE;

	return opcode;
}

/*
Puts the operands specified by the instruction in the given CPU's IR into the CPU's
ALU registers (A and B) and returns the value of the error code for this function.

parameter (the_cpu): Pointer to the CPU whose IR and ALU are used.

return value: The int value of the error code.
*/
int cpu_fetchOperands(CPUPtr the_cpu) {
	int reg_num_A, reg_num_B, error;
	if (the_cpu == NULL)
		return NULL_POINTER_ERROR;
	reg_num_A = (*the_cpu->IR) >> 7;
	reg_num_A = reg_num_A & 0x000F;
	reg_num_B = (*the_cpu->IR) >> 3;
	reg_num_B = reg_num_B & 0x000F;
	error = alu_setRegister(the_cpu->alu, REG_A, registerFile_getRegValue(the_cpu->registerFile, reg_num_A, &error));
	if (error)
		return error;
	error = alu_setRegister(the_cpu->alu, REG_B, registerFile_getRegValue(the_cpu->registerFile, reg_num_B, &error));
	if (error)
		return error;
	return NO_ERROR;
}

/*
Executes the instruction with the given opcode and mod, and returns the value
of the error code.

parameter (the_cpu): Pointer to the CPU used.
parameter (the_opcode): Opcode of the instruction.
parameter (the_mod): 3 bit mod value for the instruction.

return value: The int value of the error code.
*/
int cpu_executeInstruction(CPUPtr the_cpu, int the_opcode, int the_mod) {
	int error = 0;
	int value;
	ushort port_number;
	if (the_cpu == NULL)
		return NULL_POINTER_ERROR;
	switch(the_opcode) {
		case OPCODE_ALU:
			error = alu_op(the_cpu->alu, the_mod);
			if (the_mod != MOD_MUL_OP && the_mod != MOD_DIV_OP)
				*the_cpu->registerFile[((*the_cpu->IR) >> 7) & 0xF] = *the_cpu->alu->reg_R;
			else { // MULTIPLY and DIVIDE write to $R8 and $R9
				*the_cpu->registerFile[8] = *the_cpu->alu->reg_R;
				*the_cpu->registerFile[9] = *the_cpu->alu->reg_R2;
			}
			break;
		case OPCODE_ALU_SHL:
			error = alu_SHL(the_cpu->alu);
			*the_cpu->registerFile[((*the_cpu->IR) >> 7) & 0xF] = *the_cpu->alu->reg_R;
			break;
		case OPCODE_ALU_SHR:
			error = alu_SHR(the_cpu->alu);
			*the_cpu->registerFile[((*the_cpu->IR) >> 7) & 0xF] = *the_cpu->alu->reg_R;
			break;
		case OPCODE_BRC_IMM:
			if (cpu_checkFlags(the_cpu,*the_cpu->IR & 0x3)) {
				value = (*the_cpu->IR >> 2) & 0x1FF;
				if ((value >> 8) & 0x1)  // sign-extend
					value = value | 0xFF00;
				else
					value = value & 0x00FF;
				*the_cpu->PC = *the_cpu->PC + value;
			}
			break;
		case OPCODE_BRC_REG:
			if (cpu_checkFlags(the_cpu,*the_cpu->IR & 0x3)) {
				*the_cpu->PC = *the_cpu->registerFile[((*the_cpu->IR) >> 7) & 0xF];
			}
			break;
		case OPCODE_BR_IMM:
			cpu_BRI(the_cpu);
			break;
		case OPCODE_BR_REG:
			*the_cpu->PC = *the_cpu->registerFile[((*the_cpu->IR) >> 7) & 0xF];
			break;
		case OPCODE_HALT:
			the_cpu->macroState = STOP;
			break;
		case OPCODE_JSR:
			cpu_JSR(the_cpu);
			break;
		case OPCODE_JSRC:
			// Not yet implemented
			break;
		case OPCODE_LDA:
			cpu_LDA(the_cpu);
			break;
		case OPCODE_LDB:
			cpu_LDB(the_cpu, the_mod);
			break;
		case OPCODE_LDI:
			error = cpu_LDI(the_cpu);
			break;
		case OPCODE_LDW:
			cpu_LDW(the_cpu, the_mod);
			break;
		case OPCODE_NOP:
			// do nothing
			break;
		case OPCODE_RETURN:
			cpu_RET(the_cpu);
			break;
		case OPCODE_STACK_OPS:
			cpu_StackOps(the_cpu, the_mod);
			break;
		case OPCODE_STB:
			cpu_STB(the_cpu, the_mod);
			break;
		case OPCODE_STW:
			cpu_STB(the_cpu, the_mod);
			break;
		case OPCODE_SUPERVISOR:
			port_number = ((*the_cpu->IR) >> 1) & 0x3F;
			if ((the_mod & 0x1) == 0) {
				cpu_IN(the_cpu, port_number);
			} else {
				cpu_OUT(the_cpu, port_number);
			}
			break;
		case OPCODE_TRAP:
			// Not yet implemented
			break;
	}

	if (the_opcode != OPCODE_HALT)
		the_cpu->macroState = FETCH;
	return error;
}








// Store Operations

/*
Selects the appropriate STW operation based on the given mod.

parameter (the_cpu): Pointer to the CPU to be used.
parameter (the_cpu): The mod value to be used.

return value: The int value of the error code.
*/
int cpu_STW(CPUPtr the_cpu, int the_mod) {
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	switch (the_mod) {
		case MOD_ST_BASE:
			cpu_STWBase(the_cpu);
			break;
		case MOD_ST_INDEXREG:
			cpu_STWIndexReg(the_cpu);
			break;
		case MOD_ST_INDIRECT:
			cpu_STWIndirect(the_cpu);
			break;
		case MOD_ST_REG:
			cpu_STWRegister(the_cpu);
			break;
		default:
			printf(	"Not a valid mod for STW instruction");
			return ILLEGAL_ARG;
	}
	return NO_ERROR;
}

/*
Selects the appropriate STB operation based on the given mod.

parameter (the_cpu): Pointer to the CPU to be used.
parameter (the_cpu): The mod value to be used.

return value: The int value of the error code.
*/
int cpu_STB(CPUPtr the_cpu, int the_mod) {

	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	switch (the_mod) {
		case MOD_ST_REG:
			cpu_STBRegister(the_cpu);
			break;
		case MOD_ST_BASE:
			cpu_STBBase(the_cpu);
			break;
		case MOD_ST_INDIRECT:
			cpu_STBIndirect(the_cpu);
			break;
//		case MOD_ST_INDEXREG:
		default:
			cpu_STBIndexReg(the_cpu);
			break;
		
//		default:
//			printf("%d Not a valid mod for STB instruction", the_mod);
//			return ILLEGAL_ARG;
	}
	return NO_ERROR;
}

/*
Stores the word in the register indicated by the instruction to the location in memory
indicated by the instruction, then returns the error code value. 
The instruction is in the IR of the given CPU. This store uses a register addressing mode.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_STWRegister(CPUPtr the_cpu) {
	int src_reg_num, address_reg_num, error;
	ushort address_to_store_to, word_to_store;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	error = cpu_getRegNumbersForStore(the_cpu, &src_reg_num, &address_reg_num); 
	if (error) {
		return error;
	}
	address_to_store_to = registerFile_getRegValue(the_cpu->registerFile, address_reg_num, &error);
	if (error) {
		return error;
	}
	word_to_store = registerFile_getRegValue(the_cpu->registerFile, src_reg_num, &error);
	if(error) {
		return error;
	}
	return memory_storeWordTo(the_cpu->mainMemory, address_to_store_to, word_to_store);
}

/*
Stores the low-order byte in the register indicated by the instruction to the location in memory
indicated by the instruction, then returns the error code value. 
The instruction is in the IR of the given CPU. This store uses a register addressing mode.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_STBRegister(CPUPtr the_cpu) {
	int src_reg_num, address_reg_num, error;
	ushort address_to_store_to;
	uchar byte_to_store;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	error = cpu_getRegNumbersForStore(the_cpu, &src_reg_num, &address_reg_num);
	if (error) {
		return error;
	}
	address_to_store_to = registerFile_getRegValue(the_cpu->registerFile, address_reg_num, &error);
	byte_to_store = registerFile_getByteFrom(the_cpu->registerFile, src_reg_num, LOW_BYTE, &error);

	return memory_storeByteTo(the_cpu->mainMemory, address_to_store_to, byte_to_store);
}

/*
Stores the word in the register indicated by the instruction to the location in memory
indicated by the instruction, then returns the error code value. 
The instruction is in the IR of the given CPU. This store uses a base pointer addressing mode.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_STWBase(CPUPtr the_cpu) {
	int src_reg_num, address_reg_num, error;
	ushort address_to_store_to, address_to_add, base_pointer, word_to_store;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	error = cpu_getRegNumbersForStore(the_cpu, &src_reg_num, &address_reg_num);
	if (error) {
		return error;
	}
	address_to_add = registerFile_getRegValue(the_cpu->registerFile, address_reg_num, &error);
	if (error) {
		return error;
	}
	base_pointer = registerFile_getRegValue(the_cpu->registerFile, BASE_POINTER_REG_NUM, &error);
	if (error) {
		return error;
	}
	address_to_store_to = address_to_add + base_pointer;
	word_to_store = registerFile_getRegValue(the_cpu->registerFile, src_reg_num, &error);
	if (error) {
		return error;
	}
	return memory_storeWordTo(the_cpu->mainMemory, address_to_store_to, word_to_store);
}

/*
Stores the low-order byte in the register indicated by the instruction to the location in memory
indicated by the instruction, then returns the error code value. 
The instruction is in the IR of the given CPU. This store uses a base pointer addressing mode.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_STBBase(CPUPtr the_cpu) {
	int src_reg_num, address_reg_num, error;
	ushort address_to_store_to, address_to_add, base_pointer;
	uchar byte_to_store;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	error = cpu_getRegNumbersForStore(the_cpu, &src_reg_num, &address_reg_num);
	if (error) {
		return error;
	}
	address_to_add = registerFile_getRegValue(the_cpu->registerFile, address_reg_num, &error);
	if (error) {
		return error;
	}
	base_pointer = registerFile_getRegValue(the_cpu->registerFile, BASE_POINTER_REG_NUM, &error);
	if (error) {
		return error;
	}
	address_to_store_to = address_to_add + base_pointer;
	byte_to_store = registerFile_getByteFrom(the_cpu->registerFile, src_reg_num, LOW_BYTE, &error);
	if (error) {
		return error;
	}
	return memory_storeByteTo(the_cpu->mainMemory, address_to_store_to, byte_to_store);
}

/*
Stores the word in the register indicated by the instruction to the location in memory
indicated by the instruction, then returns the error code value. 
The instruction is in the IR of the given CPU. This store uses an index register addressing mode.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_STWIndexReg(CPUPtr the_cpu) {
	int error;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	cpu_STWRegister(the_cpu);
	error = cpu_incrementRegister(the_cpu, DEST_INDEX_REG_NUM);
	if (error) {
		return error;
	}
	return cpu_incrementRegister(the_cpu, DEST_INDEX_REG_NUM);
}

/*
Stores the low-order byte in the register indicated by the instruction to the location in memory
indicated by the instruction, then returns the error code value. 
The instruction is in the IR of the given CPU. This store uses an index register addressing mode.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_STBIndexReg(CPUPtr the_cpu) {
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	cpu_STBRegister(the_cpu);
	return cpu_incrementRegister(the_cpu, DEST_INDEX_REG_NUM);
}

/*
Stores the word in the register indicated by the instruction to the location in memory
indicated by the instruction, then returns the error code value. 
The instruction is in the IR of the given CPU. This store uses an indirect addressing mode.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_STWIndirect(CPUPtr the_cpu) {
	int src_reg_num, address_reg_num, error;
	ushort address_to_store_to, intermediate_address, word_to_store;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	error = cpu_getRegNumbersForStore(the_cpu, &src_reg_num, &address_reg_num); 
	if (error) {
		return error;
	}
	intermediate_address = registerFile_getRegValue(the_cpu->registerFile, address_reg_num, &error);
	if (error) {
		return error;
	}
	address_to_store_to = memory_fetchWordFrom(the_cpu->mainMemory, intermediate_address, &error);
	if (error) {
		return error;
	}
	word_to_store = registerFile_getRegValue(the_cpu->registerFile, src_reg_num, &error);
	if (error) {
		return error;
	}
	return memory_storeWordTo(the_cpu->mainMemory, address_to_store_to, word_to_store);
}

/*
Stores the low-order byte in the register indicated by the instruction to the location in memory
indicated by the instruction, then returns the error code value. 
The instruction is in the IR of the given CPU. This store uses an indirect addressing mode.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_STBIndirect(CPUPtr the_cpu) {
	int src_reg_num, address_reg_num, error;
	ushort address_to_store_to, intermediate_address;
	uchar byte_to_store;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	error = cpu_getRegNumbersForStore(the_cpu, &src_reg_num, &address_reg_num);
	if (error) {
		return error;
	}
	intermediate_address = registerFile_getRegValue(the_cpu->registerFile, address_reg_num, &error);
	if (error) {
		return error;
	}
	address_to_store_to = memory_fetchWordFrom(the_cpu->mainMemory, intermediate_address, &error);
	if (error) {
		return error;
	}
	byte_to_store = registerFile_getByteFrom(the_cpu->registerFile, src_reg_num, LOW_BYTE, &error);
	if (error) {
		return error;
	}
	return memory_storeByteTo(the_cpu->mainMemory, address_to_store_to, byte_to_store);
}

/*
Sets the contents of the given int pointers to the register numbers extracted from the
instruction in the given CPU. Returns the value of the error code.

parameter (the_cpu): Pointer to the CPU used.
parameter (the_reg_num_dest_ptr): Pointer to the int value of the source register number
indicated by the instruction.
parameter (the_reg_num_address_ptr); Pointer to the int value of the address register number
indicated by the instruction.

return value: The int value of the error code. 
*/
int cpu_getRegNumbersForStore(CPUPtr the_cpu, int* the_reg_num_src_ptr, int* the_reg_num_address_ptr) {
	if (the_cpu == NULL || the_reg_num_src_ptr == NULL || the_reg_num_address_ptr == NULL) {
		return NULL_POINTER_ERROR;
	}
	*the_reg_num_src_ptr = (*the_cpu->IR) >> 7;
	*the_reg_num_src_ptr = *the_reg_num_src_ptr & 0x000F;
	*the_reg_num_address_ptr = (*the_cpu->IR) >> 3;
	*the_reg_num_address_ptr = *the_reg_num_address_ptr & 0x000F;
	return NO_ERROR;
}








// Load Operations


int cpu_LDA(CPUPtr the_cpu) {
	int reg_num_dest, reg_num_address, error;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	reg_num_dest = (*the_cpu->IR) >> 7;
	reg_num_dest = reg_num_dest & 0x000F;
	error = registerFile_putRegValue(the_cpu->registerFile, reg_num_dest, memory_fetchWordFrom(the_cpu->mainMemory, *the_cpu->PC, &error));
	if (error) {
		return error;
	}
	error = cpu_incrementPC(the_cpu);
	if (error) {
		return error;
	}
	return NO_ERROR;
}

/*
Calls the appropriate LDW function based on the given mod value and returns
the value of the error code.

parameter (the_cpu): Pointer to the CPU used.

return value: The int value of the error code.
*/
int cpu_LDW(CPUPtr the_cpu, int the_mod) {
	int error;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	switch (the_mod) {
		case MOD_LD_BASE:
			error = cpu_LDWBase(the_cpu);
			break;
		case MOD_LD_INDEXREG:
			error = cpu_LDWIndexReg(the_cpu);
			break;
		case MOD_LD_INDIRECT:
			error = cpu_LDWIndirect(the_cpu);
			break;
		case MOD_LD_REG:
			error = cpu_LDWRegister(the_cpu);
			break;
		case MOD_MOV_REGTOREG:
			error = cpu_MOV(the_cpu);
			break;
		default:
			printf(	"Not a valid mod for LDW instruction");
			return ILLEGAL_ARG;
	}
	return error;
}

/*
Moves the contents of a register to another register specified in the instruction.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code.
*/
int cpu_MOV(CPUPtr the_cpu) {
	int dest_reg, src_reg;
	ushort ir = *the_cpu->IR;
	dest_reg = (ir >> 7) & 0xF;
	src_reg = (ir >> 3) & 0xF;
	*the_cpu->registerFile[dest_reg] = *the_cpu->registerFile[src_reg];
	return NO_ERROR;
}

/*
Calls the appropriate LDB function based on the given mod value and returns
the value of the error code.

parameter (the_cpu): Pointer to the CPU used.

return value: The int value of the error code.
*/
int cpu_LDB(CPUPtr the_cpu, int the_mod) {
	int error;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	switch (the_mod) {
		case MOD_LD_REG:
			error = cpu_LDBRegister(the_cpu);
			break;
		case MOD_LD_BASE:
			error = cpu_LDBBase(the_cpu);
			break;
		case MOD_LD_INDEXREG:
			error = cpu_LDBIndexReg(the_cpu);
			break;
		case MOD_LD_INDIRECT:
			error = cpu_LDBIndirect(the_cpu);
			break;
		default:
			printf(	"Not a valid mod for LDB instruction");
			return ILLEGAL_ARG;
	}
	return error;
}

/*
Loads a word from memory into the register indicated by the instruction. The instruction
comes from the given CPU's IR. This load uses base pointer addressing mode.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code.
*/
int cpu_LDWBase(CPUPtr the_cpu) {
	int reg_num_dest, reg_num_address, error;
	ushort address_of_word, base_pointer, address_to_add, word_to_load;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	cpu_getRegNumbersForLoad(the_cpu, &reg_num_dest, &reg_num_address);
	address_to_add = registerFile_getRegValue(the_cpu->registerFile, reg_num_address, &error);
	if (error) {
		return error;
	}
	base_pointer = registerFile_getRegValue(the_cpu->registerFile, BASE_POINTER_REG_NUM, &error);
	if (error) {
		return error;
	}
	address_of_word = address_to_add + base_pointer;
	word_to_load = memory_fetchWordFrom(the_cpu->mainMemory, address_of_word, &error);
	if (error) {
		return error;
	}
	return registerFile_putRegValue(the_cpu->registerFile, reg_num_dest, word_to_load);
}

/*
Loads a byte from memory into the low-order byte of the register indicated by the instruction. The instruction
comes from the given CPU's IR. This load uses a base pointer addressing mode.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code. 
*/
int cpu_LDBBase(CPUPtr the_cpu) {
	int reg_num_dest, reg_num_address, error;
	ushort address_of_byte, base_pointer, address_to_add;
	uchar byte_to_load;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	cpu_getRegNumbersForLoad(the_cpu, &reg_num_dest, &reg_num_address);
	address_to_add = registerFile_getRegValue(the_cpu->registerFile, reg_num_address, &error);
	if (error) {
		return error;
	}
	base_pointer = registerFile_getRegValue(the_cpu->registerFile, BASE_POINTER_REG_NUM, &error);
	if (error) {
		return error;
	}
	address_of_byte = address_to_add + base_pointer;
	byte_to_load = memory_fetchByteFrom(the_cpu->mainMemory, address_of_byte, &error);
	return registerFile_putByteTo(the_cpu->registerFile, reg_num_dest, LOW_BYTE, byte_to_load);
}

/*
Loads a word from memory into the register indicated by the instruction and returns the
value of the error code. The instruction comes from the given CPU's IR.
This load uses an index register addressing mode.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code. 
*/
int cpu_LDWIndexReg(CPUPtr the_cpu) {
	int error;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	error = cpu_LDWRegister(the_cpu);
	if (error) {
		return error;
	}
	error = cpu_incrementRegister(the_cpu, SOURCE_INDEX_REG_NUM); // index register incremented
	if (error) {
		return error;
	}
	return cpu_incrementRegister(the_cpu, SOURCE_INDEX_REG_NUM); // index register incremented again
}

/*
Loads a byte from memory into the low-order byte of the register indicated by the instruction and returns the
value of the error code. The instruction comes from the given CPU's IR.
This load uses an index register addressing mode.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code. 
*/
int cpu_LDBIndexReg(CPUPtr the_cpu) {
	int error;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	error = cpu_LDBRegister(the_cpu);
	return cpu_incrementRegister(the_cpu, SOURCE_INDEX_REG_NUM); // index register incremented
}




/*
Loads a word from memory into the register indicated by the instruction. The instruction
comes from the given CPU's IR. This load uses register addressing mode.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code.
*/
int cpu_LDWRegister(CPUPtr the_cpu) {
	int reg_num_dest, reg_num_address, error;
	ushort address_of_word, word_to_load;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	cpu_getRegNumbersForLoad(the_cpu, &reg_num_dest, &reg_num_address);
	address_of_word = registerFile_getRegValue(the_cpu->registerFile, reg_num_address, &error);
	if (error) {
		return error;
	}
	word_to_load = memory_fetchWordFrom(the_cpu->mainMemory, address_of_word, &error);
	if (error) {
		return error;
	}
	return registerFile_putRegValue(the_cpu->registerFile, reg_num_dest, word_to_load);
}

/*
Loads a byte from memory into the low-order byte of the register indicated by the instruction. The instruction
comes from the given CPU's IR. This load uses register addressing mode.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code.
*/
int cpu_LDBRegister(CPUPtr the_cpu) {
	int reg_num_dest, reg_num_address, error;
	ushort address_of_byte;
	uchar byte_to_load;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	cpu_getRegNumbersForLoad(the_cpu, &reg_num_dest, &reg_num_address);
	address_of_byte = registerFile_getRegValue(the_cpu->registerFile, reg_num_address, &error);
	if (error) {
		return error;
	}
	byte_to_load = memory_fetchByteFrom(the_cpu->mainMemory, address_of_byte, &error);
	return registerFile_putRegValue(the_cpu->registerFile, reg_num_dest, (ushort)byte_to_load);
}

/*
Loads a word from memory into the register indicated by the instruction. The instruction
comes from the given CPU's IR. This load uses indirect addressing mode.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code.
*/
int cpu_LDWIndirect(CPUPtr the_cpu) {
	int reg_num_dest, reg_num_address, error;
	ushort intermediate_address, address_of_word, word_to_load;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	cpu_getRegNumbersForLoad(the_cpu, &reg_num_dest, &reg_num_address);
	intermediate_address = registerFile_getRegValue(the_cpu->registerFile, reg_num_address, &error);
	if(error) {
		return error;
	}
	address_of_word = memory_fetchWordFrom(the_cpu->mainMemory, intermediate_address, &error);
	word_to_load = memory_fetchWordFrom(the_cpu->mainMemory, address_of_word, &error);
	return registerFile_putRegValue(the_cpu->registerFile, reg_num_dest, word_to_load);
}

/*
Loads a byte from memory into the low-order byte of the register indicated by the instruction. The instruction
comes from the given CPU's IR. This load uses indirect addressing mode.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code.
*/
int cpu_LDBIndirect(CPUPtr the_cpu) {
	int reg_num_dest, reg_num_address, error;
	ushort intermediate_address, address_of_byte;
	uchar byte_to_load;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	cpu_getRegNumbersForLoad(the_cpu, &reg_num_dest, &reg_num_address);
	intermediate_address = registerFile_getRegValue(the_cpu->registerFile, reg_num_address, &error);
	if (error) {
		return error;
	}
	address_of_byte = memory_fetchWordFrom(the_cpu->mainMemory, intermediate_address, &error);
	byte_to_load = memory_fetchByteFrom(the_cpu->mainMemory, address_of_byte, &error);
	return registerFile_putByteTo(the_cpu->registerFile, reg_num_dest, LOW_BYTE, byte_to_load);
}

/*
Loads the sign extended immediate value into the register indicated by the instruction, and
returns the value of the error code. The instruction is contained in the given CPU's IR.

parameter (the_cpu): Pointer to the CPU to use.

return value: The int value of the error code.
*/
int cpu_LDI(CPUPtr the_cpu) {
	ushort value;
	if (the_cpu == NULL)
		return NULL_POINTER_ERROR;
	if ((*the_cpu->IR >> 6) & 0x1)  // sign-extend
		value = *the_cpu->IR | 0xFFC0;
	else
		value = *the_cpu->IR & 0x003F;
	*the_cpu->registerFile[((*the_cpu->IR) >> 7) & 0xF] = value;
	return NO_ERROR;
}

/*
Increments the value in the given register of the given CPU.

parameter (the_cpu): Pointer to the CPU whose register is incremented.
parameter (the_register_number): The number of the register to increment.

return value: The int value of the error code. 
*/
int cpu_incrementRegister(CPUPtr the_cpu, int the_register_number) {
	int error;
	ushort current_reg_value;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	if (the_register_number < 0 || the_register_number > HIGHEST_REGFILE_INDEX) {
		return INVALID_REG_NUM;
	}
	current_reg_value = registerFile_getRegValue(the_cpu->registerFile, the_register_number, &error);
	if (error) {
		return error;
	}
	return registerFile_putRegValue(the_cpu->registerFile, the_register_number, current_reg_value+1);
}

/*
Sets the contents of the given int pointers to the register numbers extracted from the
instruction in the given CPU. Returns the value of the error code.

parameter (the_cpu): Pointer to the CPU used.
parameter (the_reg_num_dest_ptr): Pointer to the int value of the destination register number
indicated by the instruction.
parameter (the_reg_num_address_ptr); Pointer to the int value of the address register number
indicated by the instruction.

return value: The int value of the error code. 
*/
int cpu_getRegNumbersForLoad(CPUPtr the_cpu, int* the_reg_num_dest_ptr, int* the_reg_num_address_ptr) {
	if (the_cpu == NULL || the_reg_num_dest_ptr == NULL || the_reg_num_address_ptr == NULL) {
		return NULL_POINTER_ERROR;
	}
	*the_reg_num_dest_ptr = (*the_cpu->IR) >> 7;
	*the_reg_num_dest_ptr = *the_reg_num_dest_ptr & 0x000F;
	*the_reg_num_address_ptr = (*the_cpu->IR) >> 3;
	*the_reg_num_address_ptr = *the_reg_num_address_ptr & 0x000F;
	return NO_ERROR;
}







// Branch Operations

/*
Branches to the address at PC + 1 + offset as indicated by
the instruction, then returns the value of the erorr code. 
The PC used is in the given CPU.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_BRI(CPUPtr the_cpu) {
	ushort offset;
	if (the_cpu == NULL)
		return NULL_POINTER_ERROR;
	if ((*the_cpu->IR >> 10) & 0x1)  // sign-extend
		offset = *the_cpu->IR | 0xFC00;
	else
		offset = *the_cpu->IR & 0x03FF;
	*the_cpu->PC = *the_cpu->PC + offset;
	return NO_ERROR;
}





// JSR and RET Operations

/*
Executes a JSR with the given CPU and returns the error code.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_JSR(CPUPtr the_cpu) {
	int error, address_reg_num;
	ushort subroutine_address, stack_pointer;
	uchar pc_low_byte, pc_high_byte;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	address_reg_num = (*the_cpu->IR) >> 7;
	address_reg_num = address_reg_num & 0x000F;
	subroutine_address = *the_cpu->registerFile[address_reg_num];
	(*the_cpu->registerFile[STACK_POINTER_REG_NUM]) -= 2;
	memory_storeWordTo(the_cpu->mainMemory,(*the_cpu->registerFile[STACK_POINTER_REG_NUM]),*the_cpu->PC);
	return cpu_setPC(the_cpu, subroutine_address);
}

/*
Executes a RET with the given CPU and returns the error code.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_RET(CPUPtr the_cpu) {
	int error;
	ushort stack_pointer;
	uchar high_byte_popped, low_byte_popped;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	stack_pointer = registerFile_getRegValue(the_cpu->registerFile, STACK_POINTER_REG_NUM, &error);
	if (error) {
		return error;
	}
	cpu_AddValToStackPointer(the_cpu, 2);
	return cpu_setPC(the_cpu, memory_fetchWordFrom(the_cpu->mainMemory, stack_pointer, &error));
}






// Stack Operations

/*
Selects the appropriate stack operation with the given mod.

parameter (the_cpu): Pointer to the CPU to be used.
parameter (the_mod): The mod value to be used.

return value: The int value of the error code.
*/
int cpu_StackOps(CPUPtr the_cpu, int the_mod) {
	int error;
	if(the_cpu = NULL) {
		return NULL_POINTER_ERROR;
	}
	switch (the_mod) {
		case MOD_PUSHW:
			error = cpu_PUSHW(the_cpu);
			break;
		case MOD_PUSHB:
			error = cpu_PUSHB(the_cpu);
			break;
		case MOD_POPW:
			error = cpu_POPW(the_cpu);
			break;
		case MOD_POPB:
			error = cpu_POPB(the_cpu);
			break;
		default:
			printf ("Invalid mod for stack operation");
			return ILLEGAL_ARG;
	}
	return error;
}


/*
Pushes the word indicated by the instruction onto the stack, preserving
big endian order.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_PUSHW(CPUPtr the_cpu) {
	int src_reg_num, error;
	ushort stack_pointer;
	uchar high_byte, low_byte;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	stack_pointer = registerFile_getRegValue(the_cpu->registerFile, STACK_POINTER_REG_NUM, &error);
	if (error) {
		return error;
	}
	src_reg_num = (*the_cpu->IR) >> 7;
	src_reg_num = src_reg_num & 0x000F;
	low_byte = registerFile_getByteFrom(the_cpu->registerFile, src_reg_num, LOW_BYTE, &error);
	if (error) {
		return error;
	}
	high_byte = registerFile_getByteFrom(the_cpu->registerFile, src_reg_num, HIGH_BYTE, &error);
	if (error) {
		return error;
	}
	error = cpu_AddValToStackPointer(the_cpu, -1);
	if (error) {
		return error;
	}
	error = memory_storeByteTo(the_cpu->mainMemory, stack_pointer, low_byte);
	if (error) {
		return error;
	}
	error = cpu_AddValToStackPointer(the_cpu, -1);
	if (error) {
		return error;
	}
	error = memory_storeByteTo(the_cpu->mainMemory, stack_pointer, high_byte);
	return error;
}

/*
Pushes the byte indicated by the instruction onto the stack.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_PUSHB(CPUPtr the_cpu) {
	int src_reg_num, error;
	ushort stack_pointer;
	uchar byte_to_push;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	stack_pointer = registerFile_getRegValue(the_cpu->registerFile, STACK_POINTER_REG_NUM, &error);
	if (error) {
		return error;
	}
	src_reg_num = (*the_cpu->IR) >> 7;
	src_reg_num = src_reg_num & 0x000F;
	byte_to_push = registerFile_getByteFrom(the_cpu->registerFile, src_reg_num, LOW_BYTE, &error);
	error = cpu_AddValToStackPointer(the_cpu, -1);
	if (error) {
		return error;
	}
	error = memory_storeByteTo(the_cpu->mainMemory, stack_pointer, byte_to_push);
	return error;
}

/*
Pops a word off the stack to the register indicated by the instruction.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_POPW(CPUPtr the_cpu) {
	int dest_reg_num, error;
	ushort stack_pointer;
	uchar high_byte_popped, low_byte_popped;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	stack_pointer = registerFile_getRegValue(the_cpu->registerFile, STACK_POINTER_REG_NUM, &error);
	if (error) {
		return error;
	}
	dest_reg_num = (*the_cpu->IR) >> 7;
	dest_reg_num = dest_reg_num & 0x000F;
	high_byte_popped = memory_fetchByteFrom(the_cpu->mainMemory, stack_pointer, &error);
	if (error) {
		return error;
	}
	error = cpu_AddValToStackPointer(the_cpu, 1);
	if (error) {
		return error;
	}
	low_byte_popped = memory_fetchByteFrom(the_cpu->mainMemory, stack_pointer, &error);
	if (error) {
		return error;
	}
	error = cpu_AddValToStackPointer(the_cpu, 1);
	if (error) {
		return error;
	}
	error = registerFile_putByteTo(the_cpu->registerFile, dest_reg_num, HIGH_BYTE, high_byte_popped);
	if (error) {
		return error;
	}
	return registerFile_putByteTo(the_cpu->registerFile, dest_reg_num, LOW_BYTE, low_byte_popped);
}

/*
Pops a byte off the stack into the low order byte of the register indicated by the instruction.

parameter (the_cpu): Pointer to the CPU to be used.

return value: The int value of the error code.
*/
int cpu_POPB(CPUPtr the_cpu) {
	int dest_reg_num, error;
	ushort stack_pointer;
	uchar byte_popped;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	stack_pointer = registerFile_getRegValue(the_cpu->registerFile, STACK_POINTER_REG_NUM, &error);
	if (error) {
		return error;
	}
	dest_reg_num = (*the_cpu->IR) >> 7;
	dest_reg_num = dest_reg_num & 0x000F;
	byte_popped = memory_fetchByteFrom(the_cpu->mainMemory, stack_pointer, &error);
	if (error) {
		return error;
	}
	error = cpu_AddValToStackPointer(the_cpu, 1);
	if (error) {
		return error;
	}
	return registerFile_putByteTo(the_cpu->registerFile, dest_reg_num, LOW_BYTE, byte_popped);
}

/*
Adds the given value to the stack pointer and returns the error code value.
If a negative value is given, the stack pointer is decremented, moving it towards
memory[0]. If a positive value is given, the stack pointer is incremented, moving
it away from memory[0].

parameter (the_cpu): Pointer to the CPU used.
parameter (the_value): The value added to the stack pointer.

return value: The int value of the error code.
*/
int cpu_AddValToStackPointer(CPUPtr the_cpu, int the_value) {
	int error;
	return registerFile_putRegValue(the_cpu->registerFile,
				   				    STACK_POINTER_REG_NUM,
									registerFile_getRegValue(the_cpu->registerFile, STACK_POINTER_REG_NUM, &error) + the_value);
}



// IN and OUT Operations

int cpu_IN(CPUPtr the_cpu, ushort port_number) {
	int src_reg_num, error;
	char in_char;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	in_char = getch();
	memory_IOStoreByteTo(the_cpu->IOPORTS, port_number, in_char);
	src_reg_num = (*the_cpu->IR) >> 7;
	src_reg_num = src_reg_num & 0x000F;
	registerFile_putByteTo(the_cpu->registerFile, src_reg_num, LOW_BYTE, memory_IOFetchByteFrom(the_cpu->IOPORTS, port_number, &error));
	return NO_ERROR;
}

int cpu_OUT(CPUPtr the_cpu, ushort port_number) {
	int dest_reg_num, error;
	if (the_cpu == NULL) {
		return NULL_POINTER_ERROR;
	}
	dest_reg_num = (*the_cpu->IR) >> 7;
	dest_reg_num = dest_reg_num & 0x000F;
	memory_IOStoreByteTo(the_cpu->IOPORTS, port_number, registerFile_getByteFrom(the_cpu->registerFile, dest_reg_num, LOW_BYTE, &error));
//	register_putRegValue(the_cpu->PORT6, registerFile_getByteFrom(the_cpu->registerFile, dest_reg_num, LOW_BYTE, &error));	
//	addch((char) register_getLowByte(the_cpu->PORT6, &error));
	if (the_cpu->IOPORTS[port_number] == 0x0A || the_cpu->IOPORTS[port_number] == 0x0D) {
		the_cpu->cursorY++;
		the_cpu->cursorX = 0;
	}
	else {
//		addch(register_getLowByte(the_cpu->PORT6, &error));
		
		move(the_cpu->cursorY, the_cpu->cursorX);
		addch(memory_IOFetchByteFrom(the_cpu->IOPORTS, port_number, &error));
		the_cpu->cursorX++;
	}
	return NO_ERROR;
}







// Setters and Getters


/*
Checks the condition codes of the SW against the input condition codes.

return value : Nonzero integer if condition codes match; zero for false.
*/
int cpu_checkFlags(CPUPtr the_cpu,int the_cc) {
	int sw_cc = *the_cpu->SW >> 12 & 0xF;
	switch (the_cc) {
		case 0:
			return sw_cc & N_FLAG;
		case 1:
			return sw_cc & Z_FLAG;
		case 2:
			return sw_cc & C_FLAG;
		case 3:
			return sw_cc & O_FLAG;
		default:
			return 0;
	}
}

/*
Returns the value in the status word of the CPU.

parameter (the_cpu_ptr): Pointer to the CPU whose status word is returned.
parameter (the_error): Pointer to the error code value of this function.

return value: The value of the status word register.
*/
Register cpu_getSW(CPUPtr the_cpu_ptr, int* the_error) {
	if (the_cpu_ptr == NULL) {
		*the_error =  NULL_POINTER_ERROR;
		return 0;
	}
	*the_error = NO_ERROR;
	return *the_cpu_ptr->SW;
}

/*
Sets the given CPU's status word to the given value and returns the error code value.

parameter (the_cpu_ptr): Pointer to the CPU whose status word is changed.
parameter (the_status_word): Register value to be placed in the given CPU's status word.

return value: The value of the error code for this function.
*/
int cpu_setSW(CPUPtr the_cpu_ptr, Register the_status_word) {
	if (the_cpu_ptr == NULL)
		return NULL_POINTER_ERROR;
	*the_cpu_ptr->SW = the_status_word;
	return NO_ERROR;
}

/*
Returns the value in the IR of the CPU.

parameter (the_cpu_ptr): Pointer to the CPU whose IR is returned.
parameter (the_error): Pointer to the error code value of this function.

return value: The value of the IR register.
*/
Register cpu_getIR(CPUPtr the_cpu_ptr, int* the_error) {
	if (the_cpu_ptr == NULL) {
		*the_error =  NULL_POINTER_ERROR;
		return 0;
	}
	*the_error = NO_ERROR;
	return *the_cpu_ptr->IR;
}

/*
Sets the given CPU's IR to the given value and returns the error code value.

parameter (the_cpu_ptr): Pointer to the CPU whose IR is changed.
parameter (the_status_word): Register value to be placed in the given CPU's IR.

return value: The value of the error code for this function.
*/
int cpu_setIR(CPUPtr the_cpu_ptr, Register the_ir) {
	if (the_cpu_ptr == NULL)
		return NULL_POINTER_ERROR;
	*the_cpu_ptr->IR = the_ir;
	return NO_ERROR;
}

/*
Returns the value in the PC of the CPU.

parameter (the_cpu_ptr): Pointer to the CPU whose PC is returned.
parameter (the_error): Pointer to the error code value of this function.

return value: The value of the PC register.
*/
Register cpu_getPC(CPUPtr the_cpu_ptr, int* the_error) {
	if (the_cpu_ptr == NULL) {
		*the_error =  NULL_POINTER_ERROR;
		return 0;
	}
	*the_error = NO_ERROR;
	return *the_cpu_ptr->PC;
}

/*
Sets the given CPU's PC to the given value and returns the error code value.

parameter (the_cpu_ptr): Pointer to the CPU whose PC is changed.
parameter (the_status_word): Register value to be placed in the given CPU's PC.

return value: The value of the error code for this function.
*/
int cpu_setPC(CPUPtr the_cpu_ptr, Register the_pc) {
	if (the_cpu_ptr == NULL)
		return NULL_POINTER_ERROR;
	*the_cpu_ptr->PC = the_pc;
	return NO_ERROR;
}

/*
Returns the value in the MAR of the CPU.

parameter (the_cpu_ptr): Pointer to the CPU whose MAR is returned.
parameter (the_error): Pointer to the error code value of this function.

return value: The value of the MAR register.
*/
Register cpu_getMAR(CPUPtr the_cpu_ptr, int* the_error) {
	if (the_cpu_ptr == NULL) {
		*the_error =  NULL_POINTER_ERROR;
		return 0;
	}
	*the_error = NO_ERROR;
	return *the_cpu_ptr->MAR;
}

/*
Sets the given CPU's MAR to the given value and returns the error code value.

parameter (the_cpu_ptr): Pointer to the CPU whose MAR is changed.
parameter (the_status_word): Register value to be placed in the given CPU's MAR.

return value: The value of the error code for this function.
*/
int cpu_setMAR(CPUPtr the_cpu_ptr, Register the_mar) {
	if (the_cpu_ptr == NULL)
		return NULL_POINTER_ERROR;
	*the_cpu_ptr->MAR = the_mar;
	return NO_ERROR;
}

/*
Returns the value in the MDR of the CPU.

parameter (the_cpu_ptr): Pointer to the CPU whose MDR is returned.
parameter (the_error): Pointer to the error code value of this function.

return value: The value of the MDR register.
*/
Register cpu_getMDR(CPUPtr the_cpu_ptr, int* the_error) {
	if (the_cpu_ptr == NULL) {
		*the_error =  NULL_POINTER_ERROR;
		return 0;
	}
	*the_error = NO_ERROR;
	return *the_cpu_ptr->MDR;
}

/*
Sets the given CPU's MDR to the given value and returns the error code value.

parameter (the_cpu_ptr): Pointer to the CPU whose MDR is changed.
parameter (the_status_word): Register value to be placed in the given CPU's MDR.

return value: The value of the error code for this function.
*/
int cpu_setMDR(CPUPtr the_cpu_ptr, Register the_mdr) {
	if (the_cpu_ptr == NULL)
		return NULL_POINTER_ERROR;
	*the_cpu_ptr->MDR = the_mdr;
	return NO_ERROR;
}

/*
Returns the macrostate of the CPU.

parameter (the_cpu_ptr): Pointer to the CPU whose macrostate is returned.
parameter (the_error): Pointer to the error code value of this function.

return value: The value of the macrostate.
*/
int cpu_getMacroState(CPUPtr the_cpu_ptr, int* the_error) {
	if (the_cpu_ptr == NULL) {
		*the_error =  NULL_POINTER_ERROR;
		return 0;
	}
	*the_error = NO_ERROR;
	return the_cpu_ptr->macroState;
}

/*
Sets the given CPU's macrostate to the given value and returns the error code value.

parameter (the_cpu_ptr): Pointer to the CPU whose macrostate is changed.
parameter (the_status_word): int value to be placed in the given CPU's macrostate.

return value: The value of the error code for this function.
*/
int cpu_setMacroState(CPUPtr the_cpu_ptr, int the_macroState) {
	if (the_cpu_ptr == NULL)
		return NULL_POINTER_ERROR;
	the_cpu_ptr->macroState = the_macroState;
	return NO_ERROR;
}

/*
Returns the microstate of the CPU.

parameter (the_cpu_ptr): Pointer to the CPU whose microstate is returned.
parameter (the_error): Pointer to the error code value of this function.

return value: The value of the microstate.
*/
int cpu_getMicroState(CPUPtr the_cpu_ptr, int* the_error) {
	if (the_cpu_ptr == NULL) {
		*the_error =  NULL_POINTER_ERROR;
		return 0;
	}
	*the_error = NO_ERROR;
	return the_cpu_ptr->microState;
}

/*
Sets the given CPU's microstate to the given value and returns the error code value.

parameter (the_cpu_ptr): Pointer to the CPU whose microstate is changed.
parameter (the_status_word): int value to be placed in the given CPU's microstate.

return value: The value of the error code for this function.
*/
int cpu_setMicroState(CPUPtr the_cpu_ptr, int the_microState) {
	if (the_cpu_ptr == NULL)
		return NULL_POINTER_ERROR;
	the_cpu_ptr->microState = the_microState;
	return NO_ERROR;
}

/* End Program */
